/*
 *  VM.h
 *  vorpal
 *
 *  Created by rwebb on 25/04/08.
 */

#ifndef VORPAL_VM_H
#define VORPAL_VM_H

#include "Objects.h"
#include "Handle.h"
#include "System.h"
#include <list>
#include <vector>
#include <map>

typedef Vorpal::Handle<Vorpal::Object> ObjHandle;
typedef Vorpal::Handle<Vorpal::String> StrHandle;
typedef Vorpal::Handle<Vorpal::Number> NumHandle;
typedef Vorpal::Handle<Vorpal::Code> CodeHandle;

/// Everything internal to vorpal is inside this namespace.
namespace Vorpal{
	

	/// Basic Stack for object storage and access.	
	class DataStack{
	public:
		/// Create a new empty stack with space for 64 elements.
		DataStack() : depth(0), stack(64){ }
		
		/// How many objects in this stack.
		uint32_t Depth(){ return depth; }
		
		/// Adds 'Object' to the stack
		void Push(Handle<Object> &h){ 
			ExpandIfNeeded();
			stack[depth++] = h; 
		}
				
		void DropThenReplaceTop(uint numToDrop, Handle<Object> &h){ 
			Drop(numToDrop);
			FromTop(0) = h;
		}

		void DropThenReplaceTop(uint numToDrop, Object *obj){ 
			Drop(numToDrop);
			FromTop(0).Attach(obj);
		}
		
		void PushPtr(Object *object){ 
			ExpandIfNeeded();
			stack[depth++].Attach(object); 
		}
		
		/// Shortens stack depth by amount 'num' from the top down
		void Drop(uint32_t num){
			Assert(Depth() >= num);
			loop(i, depth - num, depth){
				stack[i].Detach();
			}
			depth -= num;
		}
		
		void CopyFromTop(uint32_t toIndex, uint32_t fromIndex){
			FromTop(toIndex).Detach();
			FromTop(toIndex).Attach(FromTop(fromIndex).Ptr());
		}
		
		/// Returns object at position 'index' from the stack top
		Handle<Object> &FromTop(uint32_t index){ 
			return FromBase(Depth() - index - 1); 
		}
		
		/// Returns object at position 'index' from the stack base
		Handle<Object> &FromBase(uint32_t index){ 
			Assert(index < Depth());
			return stack[index]; 
		}
		
		void RemoveFromTop(uint32_t index){ 
			stack.erase(stack.begin() + (Depth() - index - 1));
			depth -= 1;
		}

	protected:
		/// Double the size of this stack if full.
		void ExpandIfNeeded(){
			if(depth == stack.size()){
				stack.resize(2*depth);
			}
		}
		
		/// Wipes the stack.
		void Clear(){ 
			Drop(Depth());
		}
		
		void InsertAt(uint32_t index, Handle<Object> val){
			ExpandIfNeeded();
			Assert(index <= depth);
			stack.insert(stack.begin() + index, val);
			depth++;
		}
		
		/// Replaces object at 'index' with object at the top of the stack.
		void MoveTopTo(uint32_t index){
			Assert(index < depth);
			stack[index] = stack[--depth];
			stack[depth].Detach();
		}
						
	private:
		uint32_t depth;
		vector< Handle<Object> > stack;
	};

/*  Class ReturnStack
 *
 *  Purpose: 
 *
 */    

	class ReturnStack{
	public:
		void Pop(){ 
			Assert(stack.size());
			stack.pop_back();
			code.pop_back();
		}
		
		void Push(ExecutionInfo &context, Handle<Code> currentCode){ 
			stack.push_back(context); 
			code.push_back(currentCode);
		}
		
		uint32_t Depth() const{ return stack.size(); }

		const ExecutionInfo &operator[](uint index) const{
			return stack[index];
		}
		
		ExecutionInfo &Top(){ return stack.back(); }
		Handle<Code> &TopCode(){ return code.back(); }

	private:
		vector< ExecutionInfo > stack;
		vector< Handle<Code> > code;
	};
	
	class RemoteCall{
	public:
		RemoteCall(Message msg, string destId) : msg(msg), destId(destId){ }
		
		void AddArg(Handle<Object> &obj, VM *vm){
			std::stringstream strStream;
			obj->Serialize(strStream, vm);
			string s(strStream.str());
			serialArgs.push_back(s);
		}
		
		void Destination(const string &newDest){
			destId = newDest;
		}
		
		Message GetMessage() const{ return msg; }
		
		const string &Destination() const{ return destId; }
		
		const string &operator[](uint index) const{ return serialArgs[index]; }
		
		uint NumArgs() const{ return serialArgs.size(); }
		
		void AttachSequential(const RemoteCall &other){
			RemoteCall *here = this;
			while(here->sequential.size() > 0){
				here = &here->sequential[0];
			}
			here->sequential.push_back(other);
		}
		
		void AttachParallel(const RemoteCall &other){
			parallel.push_back(other);
		}
		
		void InsertResult(Handle<Object> theResult, VM *sender){
			std::stringstream strStream;
			theResult->Serialize(strStream, sender);
			loop(arg_i, 0, serialArgs.size()){
				if(ReceiveResult(arg_i, strStream.str())){
					break;
				}
			}
		}
		
		void ClearParallel(){ parallel.clear(); }
		
		vector< RemoteCall > &Parallel(){ return parallel; }
		vector< RemoteCall > &Sequential(){ return sequential; }
	
	private:
		bool ReceiveResult(uint index, const string &arg){
			if(serialArgs[index][0] == 'N'){
				serialArgs[index] = arg;
				return true;
			}
			return false;
		}
		
		Message msg;
		string destId;
		vector< string > serialArgs;
		vector< RemoteCall > parallel;
		vector< RemoteCall > sequential; // always holds 0 or 1 entry
	};
	
	class NumberAllocator{
	public:
		~NumberAllocator(){
			Clear();
		}
		
		void Clear(){
			loop(i, 0, available.size()){ delete available[i]; }		
			available.clear();
		}
		
		Number *NewNumber(double value){
			Number *result;
			if(available.size()){
				result = available.back();
				available.pop_back();
				result->Value(value);
			}
			else{
				result = new Number(value, this);
			}
			return result;			
		}
		
		void Recycle(Number *num){
			if(available.size() >= 64){ // TODO: define maxCacheSize elsewhere
				delete num;
			}
			else{
				available.push_back(num);
			}			
		}
		
		vector< Number * > available;
	};
	

/*  Class VM
 *
 *  Purpose: Basic Stack for object storage and access
 *
 *  Public Methods:     MarkRoot: 
 *                                  Execute: Starts the Virtual machine based on the cell 'next' (see common.h for cell description)
 *
 */
	class VM : public DataStack, public NumberAllocator{
	public:
		VM(Handle<Object> sys, bool registerForThreading = true);		
		virtual ~VM(){ }
		
		void SetSystem(Handle<Object> newSystem){ sys = newSystem; }
		
		Handle<Object> &GetSystem(){ return sys; }
		
		void Execute(const Cell *next, uint argsPassed, const Cell *exceptionCode = 0){
			Handle<Code> c( new Code(next) );
			Execute(c, argsPassed, exceptionCode);
		}

		void Execute(Handle<Code> startingCode, uint argsPassed, const Cell *exceptionCode = 0);

		void PrintStack();
		
		Handle<Object> Execute(Handle<Code> startingCode, Handle<Object> system, Handle<Object> self, uint numArgs = 0, Handle<Object> *args = 0){
			Handle<Object> oldSystem = sys;
			sys = system;
			
			Push(self);
			loop(i, 0, numArgs){
				Push(args[i]);
			}
			Execute(startingCode, 1 + numArgs);
			
			Handle<Object> result = FromTop(0);
			Drop(1);
			sys = oldSystem;
			return result;
		}
		
		Handle<Object> Execute(Message msg, Handle<Object> system, Handle<Object> self, uint numArgs = 0, Handle<Object> *args = 0, bool missingOK = false){
			if(!self->HasSlot(msg) || typeid(*self->Get(msg, this).Ptr()) != typeid(Code)){
				if(missingOK){
					return None();
				}
				Failure(ERR_KEY_MISSING);
			}
			Handle<Code> methodCode = Translate<Code>( self->Get(msg, this) );
			return Execute(methodCode, system, self, numArgs, args);
		}
				
		bool LoadCode(Handle<Code> &code, const string &path);
		
		bool StartStackCall(Message msg, uint numArgs){
			Handle<Code> callCode;
			if(FromTop(numArgs - 1)->IsCall(msg, callCode, this)){
				StartCall(callCode, numArgs);
				return true;
			}
			return false;
		}
		
		bool StartSystemCall(Message systemSlot, Message msg, uint numArgs){
			Handle<Code> callCode;
			if(sys->Get(systemSlot, this)->IsCall(msg, callCode, this)){
				StartCall(callCode, numArgs);
				return true;
			}
			return false;
		}

		bool ReceivedKill() const{ return receivedKill; }
		void DeferCurrentEvent(){ deferCurrentEvent = true; }
		void RedirectCurrentEvent(const string &newDest){ redirectDest = newDest; }
		uint IncomingSize() const{ return incomingCalls.size(); }
		void Incoming(const RemoteCall &rc);		
		void Outgoing(const RemoteCall &rc);
		void AttachOutgoing(const string &firstProcess, const string &secondProcess, bool attachInParallel);
		
		uint SendCalls();
		uint IncomingCount() const{ return incomingCalls.size(); }
			
		void PublishState(Handle<Object> obj){
			std::ostringstream os;
			obj->Serialize(os, this);
			pendingState = os.str();
		}
		
		bool UpdateState(){
			if(pendingState.length() > 0){
				currentState = pendingState;
				pendingState.clear();
				return true;
			}
			return false;
		}
		
		Handle<Object> GetState(VM *destVm){
			if(currentState.length() > 0){
				istringstream is(currentState);
				return Handle<Object>(Object::Unserialize(is, destVm));
			}
			return destVm->None();
		}

		void NextCall();

		bool ToBool(const Object *obj){
			return obj != None().Ptr() && obj != FromBool(false).Ptr();
		}
		
		Handle<Object> &FromBool(bool value){ 
			return value ? TrueValue : FalseValue ; 
		}
		
		Handle<Object> &None(){ 			
			return NoneValue; 
		}
		
		void PushNone(){
			Push(NoneValue);
		}
		
		void PushBool(bool value){
			if(value){
				Push(TrueValue);
			}
			else{
				Push(FalseValue);
			}
		}		
		
		/// Fills stack with new objects of type Number with amount 'num'.
		void AllocateLocals(uint32_t num){
			loop(i, 0, num){
				PushNone();
			}
		}
				
	private:		
		void CollapseVariableArgList(uint &numArgs){
			if(rs.Top().VarParamIndex() != 0 && numArgs + 1 == rs.Top().NumParams()){
				// assume the vrag is missing and insert it
				Handle< Object > array(new Object());
				InsertAt(Depth() - numArgs + rs.Top().VarParamIndex(), array);
				numArgs = rs.Top().NumParams();
			}
			else if(rs.Top().VarParamIndex() != 0 && numArgs >= rs.Top().NumParams()){
				// must create an array out of parameters
				Handle< Object > array(new Object());
				int firstIndexFromTop = numArgs - 1 - rs.Top().VarParamIndex();
				int numToAppend = 1 + numArgs - rs.Top().NumParams();
				int nextIndex = 0;
				loop(i, 0, numToAppend){
					array->Set(nextIndex++, FromTop(firstIndexFromTop));
					if(i + 1 < numToAppend){
						RemoveFromTop(firstIndexFromTop--);
					}
				}
				FromTop(firstIndexFromTop) = array;
				Assert(numArgs - numToAppend + 1 == rs.Top().NumParams());
				numArgs = rs.Top().NumParams();
			}		
		}
		
		void StartCall(Handle<Code> callCode, uint numArgs){
			ExecutionInfo context(callCode->CodePtr());
			rs.Push(context, callCode);
			
			Assert(rs.Top().NumParams() < 127 && rs.Top().NumLocals() < 127);
			CollapseVariableArgList(numArgs);
			
			if(numArgs != rs.Top().NumParams() || rs.Top().NumParams() > Depth()){
				cout << rs.Top().NumParams() << " arguments expected and " << numArgs << " received." << endl;
				Failure(ERR_INVALID_NUM_ARGS);
			}
			rs.Top().FirstLocal(Depth());
			AllocateLocals(rs.Top().NumLocals());
		}
			
		ReturnStack rs;
		Handle<Object> sys;
		string currentState;
		string pendingState;
		map< string, Handle<Code> > loadedCode;
		deque< RemoteCall > incomingCalls;
		vector< RemoteCall > outgoingCalls;
		bool deferCurrentEvent;
		string redirectDest;
		bool receivedKill;
		
		Handle<Object> FalseValue;
		Handle<Object> TrueValue;
		Handle<Object> NoneValue;
	};

	void MutateTopOfStack(VM &vm, const ExecutionInfo &context, const ReturnStack &rs);
}

#endif
